{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Lists\n",
    "\n",
    "**Time**\n",
    "- teaching: 10 min\n",
    "- exercises: 15 min\n",
    "\n",
    "**Questions**:\n",
    "- \"How do I organize several data types in an ordered manner?\"\n",
    "- \"How can I modify this collection of data?\"\n",
    "\n",
    "**Learning Objectives**:\n",
    "- \"Understand how to create and modify a list\"\n",
    "- \"Understand what a list can and can't do\"\n",
    "- \"Become familiar with common list methods\"\n",
    "* * * * *\n",
    "\n",
    "A list is an ordered, indexable collection of data. Lets say you're doing a study on the following countries:\n",
    "\n",
    "    country:\n",
    "    \n",
    "    \"Afghanistan\"\n",
    "    \"Canada\"\n",
    "    \"Thailand\"\n",
    "    \"Denmark\"\n",
    "    \"Japan\"\n",
    "\n",
    "You could put that data into a list \n",
    "\n",
    "* contain data in square brackets `[...]`, \n",
    "* each value is separated by a comma `,`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "country_list = [\"Afghanistan\", \"Canada\", \"Thailand\", \"Denmark\", \"Japan\"]\n",
    "type(country_list)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* Use `len` to find out how many values are in a list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "len(country_list)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Use an item’s [index](https://github.com/dlab-berkeley/python-intensive/blob/master/Glossary.md#index) to fetch it from a list.\n",
    "\n",
    "* Each value in a list is stored in a particular location.\n",
    "* Locations are numbered from 0 rather than 1.\n",
    "* Use the location’s index in square brackets to access the value it contains."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('the first item is:', country_list[0])\n",
    "print('the fourth item is:', country_list[3])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* Lists can be indexed from the back using a negative index. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(country_list[-1])\n",
    "print(country_list[-2])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## \"Slice\" a list using `[ : ]`\n",
    "\n",
    "* Just as with strings, we can get multiple items from a list using slicing\n",
    "* Note that the first index is included, while the second is excluded"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(country_list[1:4])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* Leave an index blank to get everything from the beginning / end"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(country_list[:4])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(country_list[2:])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lists’ values can be replaced by assigning to specific indices."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "country_list[0] = \"Iran\"\n",
    "print('Country List is now:', country_list)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* This makes lists different from strings. \n",
    "* You cannot change the characters in a [string](https://github.com/dlab-berkeley/python-intensive/blob/master/Glossary.md#string) after it has been created.\n",
    "    *   [*Immutable*](https://github.com/dlab-berkeley/python-fundamentals/blob/master/Glossary.md#immutable): cannot be changed after creation.\n",
    "    *   In contrast, lists are [*mutable*](https://github.com/dlab-berkeley/python-fundamentals/blob/master/Glossary.md#mutable): they can be modified in place."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mystring = \"Donut\"\n",
    "mystring[0] = 'C'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Mutability has consequences.\n",
    "\n",
    "The fact that lists are mutable also means that any other variables pointing to a list will *also* changed if that list gets changed:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "new_list = country_list\n",
    "print(\"original list: \", country_list)\n",
    "print(\"new list: \", new_list)\n",
    "print()\n",
    "\n",
    "country_list[0] = \"India\"\n",
    "print(\"original list: \", country_list)\n",
    "print(\"new list: \", new_list)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But it goes both ways! If a variable pointing to that list is changed, then the original list itself is changed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "new_list[0] = \"Austria\"\n",
    "print(\"original list: \", country_list)\n",
    "print(\"new list: \", new_list)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Slicing creates copies.\n",
    "On the other hand, variables created by assigning their value to a slice of a list are \"copies\" of that slice of the list, and they *do not* change when the original list gets changed:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "derived_list = country_list[:3]\n",
    "print(\"first three countries:\", derived_list)\n",
    "\n",
    "country_list[1] = \"Thailand\"\n",
    "print(\"changed country_list: \", country_list)\n",
    "\n",
    "print(\"first three countries:\", derived_list, \"...still!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So if you want to make an exact copy of a list that won't change when the original list does, then you can simply slice the entire list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "countries = ['Austria', 'Switzerland', 'Germany']\n",
    "\n",
    "new_countries = countries[:]\n",
    "\n",
    "print('countries', countries)\n",
    "print('new countries', new_countries)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's change the first item in `countries`, and print both lists."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "countries[0] = 'Brazil'\n",
    "\n",
    "print('countries', countries)\n",
    "print('new countries', new_countries)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Only `countries` changed! Same thing if we change `new_countries`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "new_countries[0] = 'Italy'\n",
    "\n",
    "print('countries', countries)\n",
    "print('new countries', new_countries)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "These are the normal behaviors of lists and other mutable data types in Python, but they can sometimes have unexpected results, especially with nested data structures. (If you need to make certain that data don't change, you can use the `copy` and `deepcopy` functions, but these are too much for this introduction.)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lists have Methods\n",
    "\n",
    "* Just like strings have methods, lists do too. \n",
    "   * Remember that a [method](https://github.com/dlab-berkeley/python-intensive/blob/master/Glossary.md#method) is like a function, but tied to a particular object.\n",
    "   * Use `object_name.method_name` to call methods.\n",
    "   * IPython lets us do tab completion after a dot ('.') to see what an object has to offer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "country_list."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* If you want to append items to the end of a list, use the `append` method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "country_list.append(\"United States\")\n",
    "print(country_list)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Use del to remove items from a list entirely.\n",
    "\n",
    "* `del list_name[index]` removes an item from a list and shortens the list.\n",
    "* Not a function or a method, but a statement in the language.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"original list was:\", country_list)\n",
    "del country_list[3]\n",
    "print(\"the list is now:\", country_list)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lists may contain values of different types.\n",
    "\n",
    "*   A single list may contain numbers, strings, and anything else."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "complex_list = ['life', 42, 'the universe', [1,2,3]]\n",
    "print(complex_list)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* Notice that we put a list inside of a list, which can itself be indexed. The same could be done for a string. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(complex_list[3])\n",
    "\n",
    "print(complex_list[3][0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## The empty list contains no values.\n",
    "\n",
    "*   Use `[]` on its own to represent a list that doesn't contain any values.\n",
    "    *   \"The zero of lists.\"\n",
    "*   Helpful as a starting point for collecting values\n",
    "    (which we will see in the next episode.)\n",
    "    \n",
    "## Indexing beyond the end of the collection is an error.\n",
    "\n",
    "*   Python reports an `IndexError` if we attempt to access a value that doesn't exist.\n",
    "    *   This is a kind of [runtime error](https://github.com/dlab-berkeley/python-intensive/blob/master/Day_3/15_Errors.ipynb).\n",
    "    *   Cannot be detected as the code is parsed\n",
    "        because the index might be calculated based on data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(country_list[99])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Challenge 1: Slice It\n",
    "\n",
    "If `thing` is a list and `low` and `high` are both non-negative integers like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "thing = [1,3,8,20,6, 'elephant', 'banana', 200, 2, 'list comprehension']\n",
    "low = 2\n",
    "high = 8"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1. What does `thing[low:high]` do?\n",
    "2. What does `thing[low:]` (without a value after the colon) do?\n",
    "3. What does `thing[:high]` (without a value before the colon) do?\n",
    "4. What does `thing[:]` (just a colon) do?\n",
    "5. How long is the list `thing[low:high]`, expressed in terms of `low` and `high`?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Challenge 2: Making Strides\n",
    "\n",
    "What does the following program print?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "city = 'Berkeley'\n",
    "print(city[::2])\n",
    "print(city[::-1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1. If we write a [slice](https://github.com/dlab-berkeley/python-intensive/blob/master/Glossary.md#slice) as low:high:stride, what does stride do?\n",
    "2. What expression would select all of the even-numbered items from a collection of consecutive integers?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Challenge 3: Append vs. Extend\n",
    "\n",
    "Using the program below, can you tell the difference between the `append` method and the `extend` method?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pantry_1 = ['bread', 'pasta', 'beans', 'cereal']\n",
    "pantry_2 = ['bread', 'pasta', 'beans', 'cereal']\n",
    "new_items = ['granola bars', 'cookies']\n",
    "pantry_1.append(new_items)\n",
    "pantry_2.extend(new_items)\n",
    "print('append does this:', pantry_1)\n",
    "print('extend does this:', pantry_2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Challenge 4: Index\n",
    "\n",
    "I've created a (long) list for you below. Use the `.index()` method to find out what the index number is for `Waldo`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "Wheres_Waldo = [\"Anna\", \"Shad\", \"Rachel\", \"Maura\", \"Jason\", \"Matt\", \"Konrad\", \"Justine\", \"Sarah\", \"Laura\", \\\n",
    "                \"Chelsea\", \"Nina\", \"Dierdre\", \"Julian\", \"Waldo\", \"Naniette\", \"Melissa\", \"Biz\", \"Elsa\", \"Demetria\",\\\n",
    "                \"Liz\", \"Olivia\", \"Will\", \"Ogi\", \"Melanie\", \"Jessica\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Challenge 5:  Join\n",
    "\n",
    "Read the help file (or the [Python documentation](https://docs.python.org/3/library/stdtypes.html?highlight=str.join#str.join)) for `join()`, a string method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "str.join?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Using the join method, concatenate all the values in this list into one string:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "letters = ['s', 'p', 'a', 'm']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now use the `join` method to make one string with all the names from the list `Wheres_Waldo`, which prints each name on a separate line. (HINT: Remember a new line is represented by `'\\n'`.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Reverse the order of the names:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Keypoints\n",
    "\n",
    "1. A list stores many values in a single structure.\n",
    "2. Use an item’s index to fetch it from a list.\n",
    "3. Lists’ values can be replaced by assigning to them.\n",
    "4. Appending items to a list lengthens it.\n",
    "5. Use del to remove items from a list entirely.\n",
    "6. The empty list contains no values.\n",
    "7. Lists may contain values of different types.\n",
    "8. Character strings are immutable.\n",
    "9. Indexing beyond the end of the collection is an error."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
